home *** CD-ROM | disk | FTP | other *** search
/ Collection of Internet / Collection of Internet.iso / infosrvr / dev / libhtml_.tar / SGML.c < prev    next >
C/C++ Source or Header  |  1993-01-20  |  12KB  |  534 lines

  1. /* SGML_stream.c
  2.  * $Id: SGMLstream.c,v 1.3 93/01/06 18:40:28 connolly Exp Locker: connolly $
  3.  */
  4.  
  5. /* implements... */
  6. #include "SGML.h"
  7.  
  8. /* uses ... */
  9. #include "object.h"
  10. #include <ctype.h>
  11. #include <assert.h>
  12. #include <string.h>
  13.  
  14.  
  15. VOID
  16.   SGML_parseInstance(stream, getch, document, docclass)
  17. HMStream stream;
  18. HMGetcProc *getch;
  19. HMDoc* document;
  20. CONST HMDoc_Class *docclass;
  21. {
  22.   static char RE[] = "\n";
  23.   char REbuffer[1 + SGML_LITLEN + SGML_NAMELEN + 4];
  24.   char *buffer = REbuffer + 1;
  25.   int content = SGML_MIXED;
  26.   int lookahead = EOF;
  27.   int len, read;
  28.   char gi[SGML_NAMELEN+1];
  29.   HMBinding attributes[SGML_ATTCNT];
  30.   int attrqty;
  31.   char eat_next_RE = 1, RE_pending = 0;
  32.  
  33.   REbuffer[0] = '\n'; /*@@ should be 13, not 10! */
  34.  
  35.   while( (read = SGML_read(stream, getch, buffer, sizeof(REbuffer) - 2,
  36.                content, &lookahead)) != EOF){
  37.     switch(read){
  38.     case SGML_start_tag:
  39.       if (RE_pending){
  40.     (docclass->data)(document, RE, 1);
  41.       }
  42.  
  43.       len = SGML_read_name(stream, getch, gi, &lookahead);
  44.       gi[len] = 0;
  45.  
  46.       attrqty = 0;
  47.       while(isalpha(lookahead)){ /* iterate over attributes */
  48.     len = SGML_read_name(stream, getch,
  49.                  buffer, &lookahead);
  50.     buffer[len] = 0;
  51.  
  52.     if(lookahead == '='){
  53.       int offset = len + 1;
  54.       HMBinding* attr = &attributes[attrqty++];
  55.  
  56.       lookahead = EOF;
  57.       /* @@ entity references in attribute value */
  58.       len += SGML_read_value(stream,
  59.                  getch,
  60.                  buffer + offset,
  61.                  &lookahead) + 1;
  62.       buffer[len++] = '\0';
  63.       attr->name = NEW(char, len);
  64.       memcpy(attr->name, buffer, len);
  65.       attr->value = attr->name + offset;
  66.     }
  67.       }
  68.  
  69.       /* look for tag close */
  70.       while(isspace(lookahead))
  71.     lookahead = (getch)(stream);
  72.       lookahead = EOF;
  73.  
  74.       {
  75.     int i;
  76.     int c;
  77.       
  78.     c = (docclass->startTag)(document, gi, attributes, attrqty);
  79.  
  80.     if(c == SGML_EMPTY){
  81.       eat_next_RE = 0;
  82.     }else{
  83.       content = c;
  84.       eat_next_RE = 1;
  85.     }
  86.  
  87.     for(i=0; i<attrqty; i++)
  88.       FREE(attributes[i].name);
  89.       }
  90.       RE_pending = 0;
  91.       break;
  92.  
  93.     case SGML_end_tag:
  94.       /* drop pending RE */
  95.  
  96.       len = SGML_read_name(stream, getch, gi, &lookahead);
  97.       gi[len] = 0;
  98.  
  99.       /* look for tag close */
  100.       while(isspace(lookahead))
  101.     lookahead = (getch)(stream);
  102.       lookahead = EOF;
  103.  
  104.       (docclass->endTag)(document, gi);
  105.       content = SGML_MIXED; /* @@ could be element */
  106.       eat_next_RE = 0;
  107.       RE_pending = 0;
  108.       break;
  109.  
  110.     case SGML_entity:
  111.       if (RE_pending){
  112.     (docclass->data)(document, RE, 1);
  113.       }
  114.       eat_next_RE = 0;
  115.       RE_pending = 0;
  116.  
  117.       {
  118.     CONST char* text = (docclass->entityText)(document, buffer);
  119.  
  120.     if(text)
  121.       (docclass->data)(document, text, strlen(text));
  122.       }
  123.       break;
  124.  
  125.     case SGML_record_end:
  126.       if(eat_next_RE){
  127.     eat_next_RE = 0;
  128.     RE_pending = 0;
  129.       }
  130.       else if (RE_pending){
  131.     (docclass->data)(document, RE, 1);
  132.       }
  133.       else
  134.     RE_pending = 1;
  135.  
  136.       break;
  137.  
  138.     default:
  139.       buffer[read] = 0;
  140.       if(RE_pending)
  141.     (docclass->data)(document, REbuffer, read + 1);
  142.       else
  143.     (docclass->data)(document, buffer, read);
  144.       RE_pending = 0;
  145.       eat_next_RE = 0;
  146.       break;
  147.     }
  148.   }while(read != EOF);
  149. }
  150.  
  151.  
  152. /*****
  153.  * lexical analysis
  154.  *****/
  155.  
  156. int
  157.   SGML_read(stream, getch,
  158.         buf, nbytes,
  159.         content,
  160.         inout_lookahead)
  161. HMStream stream;
  162. HMGetcProc* getch;
  163. char* buf;
  164. int nbytes;
  165. int content;
  166. int* inout_lookahead;
  167. {
  168.   int c; /* state machine input character */
  169.   enum { /* state machine states */
  170.     start, data, cdata, rcdata, pcdata,
  171.     and, and_hash, cref, entity,
  172.     lt, lt_slash, tag,
  173.     pi,
  174.     lt_bang, lt_bang_dash,
  175.     comment, comment_dash, ps
  176.   } state = start;
  177.   /* auxiliary state: */
  178.   int end_tag; /* saw '/' after '<' */
  179.   char name[SGML_NAMELEN + 1]; /* function character name */
  180.   int name_chars;
  181.  
  182.   int ret = 0; /* number of characters read */
  183.  
  184. #define LOOKAHEAD(n) (ret + n < nbytes)
  185. #define REDUCE(s) { state = (s); break; }
  186. #define SHIFT(s) { state = (s); continue; }
  187. #define DONE(c) { *inout_lookahead = (c); return ret; }
  188. #define WRITE(c) { *buf++ = (c); ret++; }
  189.  
  190.   /* prime the pump */
  191.   if((c  = *inout_lookahead) == EOF)
  192.     c = (getch)(stream);
  193.  
  194.   /* state machine...*/
  195.   while(ret < nbytes){
  196.  
  197.     switch(state){
  198.  
  199.     case start:
  200.       if(c == EOF) return EOF;
  201.       else if(c == '\n') { ret = SGML_record_end; DONE(EOF); }
  202.       else if(c == '<'){
  203.     if(LOOKAHEAD(3)) { REDUCE(lt); }
  204.     else { DONE(c); } /* no room for lookahead */
  205.       }else if(c == '&'){
  206.     if(LOOKAHEAD(2)) { REDUCE(and); }
  207.       }else if(content == SGML_ELEMENT && isspace(c)){
  208.     break; /* ignore whitespace in ELEMENT content */
  209.       }else { SHIFT(data); }
  210.  
  211.     case data:
  212.       if(content == SGML_ELEMENT){
  213.     if(isspace(c)){
  214.       break;
  215.     }else{
  216.       *buf = 0; ret = 0; DONE(c);
  217.     }
  218.       }else if(content == SGML_CDATA){ SHIFT(cdata); }
  219.       else if(content == SGML_RCDATA){ SHIFT(rcdata); }
  220.       else /* assume SGML_MIXED */ { SHIFT(pcdata); }
  221.  
  222.     case cdata:
  223.       if(c == EOF || c == '<' || c == '\n') { DONE(c); }
  224.       else{ WRITE(c);    break; }
  225.  
  226.     case rcdata:
  227.     case pcdata:
  228.       if(c == EOF || c == '<' || c == '&' || c == '\n') { DONE(c); }
  229.       else{ WRITE(c); break; }
  230.  
  231.     case and:
  232.       if(c == '#') { REDUCE(and_hash); }
  233.       else if(isalpha(c)) {
  234.     if(LOOKAHEAD(SGML_NAMELEN+1)){
  235.       name_chars = 0; SHIFT(entity);
  236.     }else{
  237.       DONE(c); /* error: no room for entity name */
  238.     }
  239.       }
  240.       else{ WRITE('&');    SHIFT(data); }
  241.  
  242.     case entity:
  243.       if(isalnum(c) || strchr(SGML_UCNMCHAR, c)){
  244.     WRITE(c);
  245.     break;
  246.       }
  247.       else{
  248.     WRITE('\0');
  249.     ret = SGML_entity;
  250.     if(c == ';' || c == '\n'){  DONE(EOF); /* eat ; */ }
  251.     else{ DONE(c); /* ended char ref with other char */ }
  252.       }
  253.  
  254.     case and_hash:
  255.       if(isalnum(c)){ name_chars = 0; SHIFT(cref); }
  256.       else{ WRITE('&'); WRITE('#'); SHIFT(data); }
  257.  
  258.     case cref:
  259.       /* auxiliary state: name_chars */
  260.       if(isalnum(c) || strchr(SGML_UCNMCHAR, c)){
  261.     if(name_chars < SGML_NAMELEN)
  262.       name[name_chars++] = c;
  263.     /* else markup error: name too long */
  264.     break;
  265.       }
  266.       else{
  267.     int nc = 0;
  268.  
  269.     name[name_chars] = '\0';
  270.     if(isdigit(name[0])){
  271.       nc = atoi(name);
  272.     }else if(!strcmp(name, "SPACE")){
  273.       nc = 32;
  274.     }else if(!strcmp(name, "RS")){
  275.       nc = 10;
  276.     }else if(!strcmp(name, "RE")){
  277.       nc = 13;
  278.     }
  279.  
  280.     if(nc) WRITE(nc); /* else error: bad character reference */
  281.  
  282.     if(c == ';') { REDUCE(data); }
  283.     else
  284.       /* terminate entity reference w/space or something */
  285.       { SHIFT(data); }
  286.       }
  287.  
  288.     case lt:
  289.       if(c == '/') { REDUCE(lt_slash); }
  290.       if(content == SGML_MIXED || content == SGML_ELEMENT){
  291.     if(c == '?') { REDUCE(pi); }
  292.     else if(c == '!') { REDUCE(lt_bang); }
  293.     else if(isalpha(c)) { end_tag = 0; SHIFT(tag); }
  294.       }
  295.       WRITE('<'); SHIFT(data);
  296.  
  297.     case lt_slash:
  298.       if(isalpha(c)) { end_tag = 1; SHIFT(tag); }
  299.       else { WRITE('<'); WRITE('/'); SHIFT(data); }
  300.  
  301.     case tag:
  302.       /* auxiliary state: end_tag */
  303.       ret = end_tag ?  SGML_end_tag : SGML_start_tag;
  304.       DONE(c);
  305.  
  306.     case pi: /* processing instruction (or markup declaraion) */
  307.       if(c == '>') { REDUCE(start); }
  308.       else if(c == EOF) { SHIFT(start); } /* error: EOF in pic */
  309.       else break;
  310.  
  311.     case lt_bang:
  312.       if(c == '-') { REDUCE(lt_bang_dash); }
  313.       /*
  314.        * *** NON CONFORMING IMPLEMENTATION ***
  315.        * a letter here starts a markup declaration, which isn't supported
  316.        * a [ starts a marked section, which isn't supported.
  317.        * treat them like processing instructions.
  318.        */
  319.       else if(c == '[' || isalpha(c)) { REDUCE(pi); }
  320.       else if(c == '>') { REDUCE(start); }
  321.       else{ WRITE('<'); WRITE('!'); SHIFT(data); }
  322.  
  323.     case lt_bang_dash:
  324.       if(c == '-') { REDUCE(comment); }
  325.       else{ WRITE('<'); WRITE('!'); WRITE('-'); SHIFT(data); }
  326.  
  327.     case comment:
  328.       if(c == '-') { REDUCE(comment_dash); }
  329.       else if(c == EOF) { DONE(c); } /* error: eof in comment */
  330.       else break;
  331.  
  332.     case comment_dash:
  333.       if(c == '-') { REDUCE(ps); }
  334.       else if(c == EOF) { DONE(c); }/* error: eof in comment */
  335.       else { REDUCE(comment); }
  336.  
  337.     case ps: /* parameter separator between -- and > */
  338.       if(c == EOF) { DONE(c); }
  339.       else if(isspace(c)) break;
  340.       else { REDUCE(start); }/* error if c !='>' */
  341.  
  342.     }
  343.     c = (getch)(stream);
  344.   }
  345.  
  346.   DONE(c); /* set up lookahead for next call */
  347. #undef S
  348. #undef LOOKAHEAD
  349. #undef REDUCE
  350. #undef SHIFT
  351. #undef DONE
  352. #undef WRITE
  353. }
  354.  
  355.  
  356. int
  357.   SGML_read_name(stream, getch, buf, inout_lookahead)
  358. HMStream stream;
  359. HMGetcProc* getch;
  360. char* buf;
  361. int* inout_lookahead;
  362. {
  363.   int name_chars = 0;
  364.   int c = *inout_lookahead;
  365.  
  366.   if(!isalpha(c)) return 0;
  367.  
  368.   do{
  369.     if(name_chars <= SGML_NAMELEN)
  370.       buf[name_chars++] = toupper(c);
  371.     /* else error: name too long */
  372.     c = (getch)(stream);
  373.   }while(isalnum(c) || strchr(SGML_UCNMCHAR, c));
  374.  
  375.   while(isspace(c))
  376.     c = (getch)(stream);
  377.  
  378.   *inout_lookahead = c;
  379.   return name_chars;
  380. }
  381.  
  382.  
  383. int
  384.   SGML_read_value (stream,
  385.            getch,
  386.            buf,
  387.            inout_lookahead)
  388. HMStream stream;
  389. HMGetcProc* getch;
  390. char* buf;
  391. int* inout_lookahead;
  392. {
  393.  
  394.   int c; /* state machine input character */
  395.   enum { /* state machine states */
  396.     start,
  397.     literal,
  398.     and, and_hash, cref,
  399. #if defined(SGML_SHORTTAG) || defined(GROK_UNQUOTED_LITERALS)
  400.     value,
  401. #endif
  402.     ps
  403.   } state = start;
  404.   /* auxiliary state: */
  405.   char quote; /* which kind of quote */
  406.  
  407.   int ret = 0; /* number of characters read */
  408.   char name[SGML_NAMELEN + 1]; /* entity name */
  409.   int name_chars;
  410.  
  411. #define LOOKAHEAD(n) (ret + n < SGML_LITLEN)
  412. #define REDUCE(s) { state = (s); break; }
  413. #define SHIFT(s) { state = (s); continue; }
  414. #define DONE(c) { *inout_lookahead = (c); return ret; }
  415. #define WRITE(c) { *buf++ = (c); ret++; }
  416.  
  417.   /* prime the pump */
  418.   if((c  = *inout_lookahead) == EOF)
  419.     c = (getch)(stream);
  420.   
  421.   /* state machine...*/
  422.   while(ret < SGML_LITLEN){
  423.  
  424.     switch(state){
  425.  
  426.     case start:
  427.       if(c == EOF) return EOF;
  428.       else if(c == '"') { quote = c; REDUCE(literal); }
  429.       else if(c == '\'') { quote = c; REDUCE(literal); }
  430.       else if(isspace(c)) break;
  431. #ifdef GROK_UNQUOTED_LITERALS
  432.       else if(!(c == '>')){
  433.     SHIFT(value);
  434.       }
  435. #else
  436. #ifdef SGML_SHORTTAG
  437.       else if(isalnum(c) || strchr(SGML_UCNMCHAR, c)){
  438.     SHIFT(value);
  439.       }
  440. #else
  441.       else { DONE(c); } /* error: illegal char in markup */
  442. #endif
  443. #endif
  444.  
  445. #ifdef GROK_UNQUOTED_LITERALS
  446.     case value:
  447.       if(c == EOF) { DONE(c); }
  448.       else if(isspace(c) || c == '>'){ SHIFT(ps); }
  449.       else{
  450.     WRITE(c);
  451.     break;
  452.       }
  453. #else
  454. #ifdef SGML_SHORTTAG
  455.     case value:
  456.       if(c == EOF) { DONE(c); }
  457.       else if(isalnum(c) || strchr(SGML_UCNMCHAR, c)){
  458.     WRITE(c);
  459.     break;
  460.       }else{ SHIFT(ps); }
  461. #endif
  462. #endif
  463.  
  464.     case literal:
  465.       if(c == EOF) { DONE(c); }
  466.       else if(c == quote) { REDUCE(ps); }
  467.       else if(c == '&'){ REDUCE(and); }
  468.       else if(c == '\n' || c == '\t'){ WRITE(' '); break; }
  469.       else{
  470.     WRITE(c);
  471.     break;
  472.       }
  473.  
  474.     case and:
  475.       if(c == '#') { REDUCE(and_hash); }
  476.       /*@@ else if(isalpha(c)) ... process entity reference */
  477.       else{ WRITE('&');    SHIFT(literal); }
  478.  
  479.     case and_hash:
  480.       if(isalnum(c)){ name_chars = 0; SHIFT(cref); }
  481.       else{ WRITE('&'); WRITE('#'); SHIFT(literal); }
  482.  
  483.     case cref:
  484.       /*@@ in case of  xyz, this throws out xyz as error, when
  485.     it should only throw out x */
  486.       if(isdigit(c) || isalpha(c)
  487.      || strchr(SGML_UCNMCHAR, c)){
  488.     if(name_chars < SGML_NAMELEN)
  489.       name[name_chars++] = c;
  490.     /* else markup error: name too long */
  491.     break;
  492.       }
  493.       else{
  494.     int nc = 0;
  495.  
  496.     name[name_chars] = '\0';
  497.     if(isdigit(name[0])){
  498.       nc = atoi(name);
  499.     }else if(!strcmp(name, "SPACE")){
  500.       nc = 32;
  501.     }else if(!strcmp(name, "RS")){
  502.       nc = 10;
  503.     }else if(!strcmp(name, "RE")){
  504.       nc = 13;
  505.     }else
  506.       break;
  507.  
  508.     if(nc) WRITE(nc); /* else error: bad character reference */
  509.  
  510.     if(c == ';') { REDUCE(literal); }
  511.     else
  512.       /* terminate entity reference w/space or something */
  513.       { SHIFT(literal); }
  514.       }
  515.  
  516.     case ps: /* parameter separator between attributes */
  517.       if(isspace(c)) break;
  518.       else { DONE(c); }
  519.  
  520.     }
  521.     c = (getch)(stream);
  522.   }
  523.  
  524.   /* error: attribute value too long */
  525.  
  526.   DONE(EOF); /* set lookahead to EOF for next call */
  527. #undef S
  528. #undef LOOKAHEAD
  529. #undef REDUCE
  530. #undef SHIFT
  531. #undef DONE
  532. #undef WRITE
  533. }
  534.